/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkTransformFeedback.h

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

// .NAME vtkTransformFeedback - Manages a TransformFeedback buffer.
//
// .SECTION Description
// OpenGL's TransformFeedback allows varying attributes from a vertex/geometry
// shader to be captured into a buffer for later processing. This is used in
// VTK to capture vertex information during GL2PS export when using the OpenGL2
// backend as a replacement for the deprecated OpenGL feedback buffer.

#ifndef vtkTransformFeedback_h
#define vtkTransformFeedback_h

#include "vtkRenderingOpenGL2Module.h" // For export macro
#include "vtkObject.h"

#include <string> // For string
#include <vector> // For vector

class vtkShaderProgram;
class vtkWindow;

class VTKRENDERINGOPENGL2_EXPORT vtkTransformFeedback : public vtkObject
{
public:
  static vtkTransformFeedback *New();
  vtkTypeMacro(vtkTransformFeedback, vtkObject)
  virtual void PrintSelf(ostream &os, vtkIndent indent);

  // Description:
  // The role a captured varying fills. Useful for parsing later.
  enum VaryingRole
    {
    Vertex_ClipCoordinate_F, // Projected XYZW
    Color_RGBA_F
    };

  struct VaryingMetaData
  {
    VaryingMetaData(VaryingRole role, const std::string &id)
      : Role(role), Identifier(id) {}
    VaryingRole Role;
    std::string Identifier;
  };

  // Description:
  // Clear the list of varying attributes to capture.
  void ClearVaryings();

  // Description:
  // Capture the varying 'var' with the indicated role.
  void AddVarying(VaryingRole role, const std::string &var);

  // Description:
  // Get the list of captured varyings,
  const std::vector<VaryingMetaData>& GetVaryings() const
  {
    return this->Varyings;
  }

  // Description:
  // Returns the number of data elements each vertex requires for a given role.
  static size_t GetBytesPerVertex(VaryingRole role);

  // Description:
  // Returns the number of bytes per vertexs, accounting for all roles.
  size_t GetBytesPerVertex() const;

  // Description:
  // The number of vertices expected to be captured. If the drawMode setter is
  // used, PrimitiveMode will also be set appropriately.
  // For the single argument version set function, set the exact number of
  // vertices expected to be emitted, accounting for primitive expansion (e.g.
  // triangle strips -> triangle strips).
  // The two argument setter is for convenience. Given the number of vertices
  // used as input to a draw command and the draw mode, it will calculate the
  // total number of vertices.
  vtkSetMacro(NumberOfVertices, size_t)
  void SetNumberOfVertices(int drawMode, size_t inputVerts);
  vtkGetMacro(NumberOfVertices, size_t)

  // Description:
  // The size (in bytes) of the capture buffer. Available after adding all
  // Varyings and setting NumberOfVertices.
  size_t GetBufferSize() const;

  /*
   * GL_SEPARATE_ATTRIBS is not supported yet.
   *
  // Description:
  // The bufferMode argument to glTransformFeedbackVaryings. Must be
  // GL_INTERLEAVED_ATTRIBS or GL_SEPARATE_ATTRIBS. Default is interleaved. Must
  // be set prior to calling BindVaryings.
  vtkSetMacro(BufferMode, int)
  vtkGetMacro(BufferMode, int)
  *
  */

  // Description:
  // Call glTransformFeedbackVaryings(). Must be called after the shaders are
  // attached to \a prog, but before the program is linked.
  void BindVaryings(vtkShaderProgram *prog);

  // Description:
  // Get the handle to the transform buffer object. Only valid after calling
  // BindBuffer and before ReadBuffer.
  vtkGetMacro(BufferHandle, int)

  // Description:
  // The type of primitive to capture. Must be one of GL_POINTS, GL_LINES, or
  // GL_TRIANGLES. Default is GL_POINTS. Must be set prior to calling
  // BindBuffer.
  vtkSetMacro(PrimitiveMode, int)
  vtkGetMacro(PrimitiveMode, int)

  // Description:
  // Generates, binds, and allocates the feedback buffer, then call
  // glBeginTransformFeedback with the specified PrimitiveMode. Must be called
  // after BindVaryings and before any relevant glDraw commands.
  void BindBuffer();

  // Description:
  // Calls glEndTransformFeedback(), flushes the OpenGL command stream, and
  // reads the transform feedback buffer into BufferData. Must be called after
  // any relevant glDraw commands.
  void ReadBuffer();

  // Description:
  // Get the transform buffer data as a void pointer. Only valid after calling
  // ReadBuffer.
  vtkGetMacro(BufferData, void*)

  // Description:
  // Release any graphics resources used by this object.
  void ReleaseGraphicsResources();

  // Description:
  // Release the memory used by the buffer data. If freeBuffer == true
  // (default), the data is deleted. If false, the caller is responsible for
  // deleting the BufferData with delete[].
  void ReleaseBufferData(bool freeBuffer = true);

protected:
  vtkTransformFeedback();
  ~vtkTransformFeedback();

private:
  vtkTransformFeedback(const vtkTransformFeedback &); // Not implemented.
  void operator=(const vtkTransformFeedback &);   // Not implemented.

  bool VaryingsBound;

  std::vector<VaryingMetaData> Varyings;
  size_t NumberOfVertices;
  int BufferMode;

  int BufferHandle;
  int PrimitiveMode;

  unsigned char *BufferData;
};

//------------------------------------------------------------------------------
inline size_t vtkTransformFeedback::GetBytesPerVertex(
    vtkTransformFeedback::VaryingRole role)
{
  switch (role)
    {
    case Vertex_ClipCoordinate_F:
      return 4 * sizeof(float);
    case Color_RGBA_F:
      return 4 * sizeof(float);
    }

  vtkGenericWarningMacro("Unknown role enum value: " << role);
  return 0;
}

#endif // vtkTransformFeedback_h
